﻿using StructuredProgrammingAssignment.App_Start;
using StructuredProgrammingAssignment.Helpers;
using StructuredProgrammingAssignment.Linq;
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Linq.Dynamic;

namespace StructuredProgrammingAssignment.Controllers
{
	public class UserStuffController : SPStuffController
	{
		private Object thisLock = new Object();

		/**
		 * Get list of all stuffs belonging to a sepcific user account.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] Get(
			string userId,						// current user id, used for authorization

			string where = null,				// dynamic filter clause, this feature can be turn off via config
			string order = null,				// dynamic order clause, this feature can be turn off via config

			int categoryId = int.MinValue,		// filter stuffs by category

			string name = null,					// filter by name
			bool nameExact = false,				// search for exact stuff name if true

			string description = null,			// filter by description
			bool descriptionExact = false,		// search for exact stuff description if true

			DateTime? modifiedFrom = null,		// set minimum modified datetime
			DateTime? modifiedTo = null,		// set maximum modified datetime
			DateTime? createdFrom = null,		// set minimum created datetime
			DateTime? createdTo = null,			// set maximum created datetime

			decimal priceMin = decimal.MinValue,	// set minimum price
			decimal priceMax = decimal.MaxValue,	// set maximum price
			bool priceExclusive = true,			// exclusive or not

			string orderCol = "Name",			// order column
			string orderDir = "ASC",			// order direction
			int offset = 0,						// specifies the number of rows to skip
			int limit = 0						// the number of rows to be retrieved
		) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId;
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Double check
				UserAccount account = (from a in db.UserAccounts
									   where a.Id.Equals(parsedUserId)
									   select a).FirstOrDefault();
				if (account == null) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Retrieve list of stuffs
				var stuffs = (from s in db.UserStuffs
							  where s.UserId.Equals(parsedUserId)
							  select s);

				// Return
				return GetStuffs(stuffs.ToList(), where, order, categoryId, name, nameExact, description, descriptionExact, modifiedFrom, modifiedTo, createdFrom, createdTo, priceMin, priceMax, priceExclusive, orderCol, orderDir, offset, limit);
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // Get

		/**
		 * Insert new user stuff.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] Insert(
			String userId,						// current user id, used for authorization
			string name,						// stuff name
			int categoryId,						// category
			decimal price,						// price (>=0)

			string description = null,			// stuff description
			int quantity = 1,					// quantity of the stuff (>=0)
			string properties = null			// custom properties
		) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId = new Guid();
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Validate inputs
				if (quantity < 0) {
					return FailureResult("Quantity must be greater than or equal to zero.", "input validation error");
				}
				if (price < 0) {
					return FailureResult("Unit price must be greater than or equal to zero.", "input validation error");
				}

				// Get category
				Category category = (from c in db.Categories
									 where c.Id == categoryId
									 select c).FirstOrDefault();
				if (category == null) {
					return FailureResult("Invalid category.", "invalid category");
				}

				// Init new stuff
				UserStuff stuff = new UserStuff() {
					UserId = parsedUserId,
					Name = name,
					Description = description,
					Quantity = quantity,
					CategoryId = categoryId,
					UnitPrice = price
				};

				// Submit changes
				lock (thisLock) {
					db.UserStuffs.InsertOnSubmit(stuff);
					db.SubmitChanges();
					stuff = db.UserStuffs.AsEnumerable().Last();
				}

				return SuccessResult(SPHelper.JsonUserStuff(stuff));
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // Insert

		/**
		 * Update an existing user stuff.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] Update(
			String userId,						// current user id, used for authorization
			int stuffId,						// id of the stuff to be updated

			string name = null,					// new stuff name
			string description = null,			// description
			string properties = null,			// custom properties
			int quantity = int.MinValue,		// quantity of the stuff (>=0)
			int categoryId = int.MinValue,		// category
			decimal price = decimal.MinValue	// stuff price (>=0)
		) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId = new Guid();
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}
				// Double check
				UserStuff stuff = (from s in db.UserStuffs
								   where s.Id == stuffId
								   select s).FirstOrDefault();
				if (stuff == null) {
					return FailureResult("No user stuff exists with that ID.", "invalid user stuff");
				}
				if (!stuff.UserId.Equals(parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Validate inputs
				if (quantity != int.MinValue && quantity < 0) {
					return FailureResult("Quantity must be greater than or equal to zero.", "input validation error");
				}
				if (price != decimal.MinValue && price <= 0) {
					return FailureResult("Unit price must be greater than zero.", "input validation error");
				}

				if (categoryId == int.MinValue && name == null && description == null && properties == null && quantity == int.MinValue) {
					LogWarning("user stuff might not be edited properly.");
				}

				// Update
				if (categoryId != int.MinValue) {
					Category category = (from c in db.Categories
										 where c.Id == categoryId
										 select c).FirstOrDefault();
					if (category == null) {
						return FailureResult("Invalid category.", "invalid category");
					}
				}
				if (name != null) {
					stuff.Name = name;
				}
				if (description != null) {
					stuff.Description = description;
				}
				if (properties != null) {
					stuff.Properties = properties;
				}
				if (quantity != int.MinValue) {
					stuff.Quantity = quantity;
				}
				if (price != decimal.MinValue) {
					stuff.UnitPrice = price;
				}
				if (categoryId != int.MinValue) {
					stuff.CategoryId = categoryId;
				}

				// Submit changes & return
				db.SubmitChanges();

				return SuccessResult(SPHelper.JsonUserStuff(stuff));
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // Update

		/**
		 * Delete stuff from database, authorized by its owner.
		 **/
		[System.Web.Http.AcceptVerbs("GET", "POST")]
		public Object[] Delete(String userId, int stuffId) {
			try {
				SPDataContext db = new SPDataContext();

				// Authorization
				Guid parsedUserId = new Guid();
				if (!Guid.TryParse(userId, out parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}
				// Double check
				UserStuff stuff = (from s in db.UserStuffs
								   where s.Id == stuffId
								   select s).FirstOrDefault();
				if (!stuff.UserId.Equals(parsedUserId)) {
					return FailureResult("You are not authorized to execute this operation.", "authorization error");
				}

				// Check for existance of the stuff
				if (stuff == null) {
					return FailureResult("Invalid user account.", "invalid user stuff");
				}

				// Delete stuff & return
				db.UserStuffs.DeleteOnSubmit(stuff);
				db.SubmitChanges();

				return SuccessResult(null);
			} catch (Exception e) {
				return ExceptionResult(e);
			}
		} // Delete

	} // UserStuffController
}